package com.authine.cloudpivot.ext.service.impl;

import ch.ethz.ssh2.ChannelCondition;
import ch.ethz.ssh2.Connection;
import ch.ethz.ssh2.Session;
import ch.ethz.ssh2.StreamGobbler;
import com.alibaba.fastjson.JSON;
import com.authine.cloudpivot.engine.api.facade.SystemManagementFacade;
import com.authine.cloudpivot.engine.api.model.system.SystemSettingModel;
import com.authine.cloudpivot.engine.enums.type.FileUploadType;
import com.authine.cloudpivot.engine.enums.type.SettingType;
import com.authine.cloudpivot.ext.service.SshService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @Author hxd
 * @Date 2022/4/30 13:12
 * @Description
 **/
@Service
@Slf4j
public class SshServiceImpl implements SshService {

    @Autowired
    SystemManagementFacade systemManagementFacade;
    private static Map<String, String> ssh2ConfigMap = new HashMap<>();

    private Map<String, String> getSsh2SessionConfig() {

        if (MapUtils.isEmpty(ssh2ConfigMap)) {
            List<SystemSettingModel> systemSettings = systemManagementFacade.getSystemSettingsBySettingType(SettingType.FILE_UPLOAD);
            ssh2ConfigMap = systemSettings.stream()
                    .filter((t) -> FileUploadType.FTP.equals(t.getFileUploadType()) && t.getChecked())
                    .collect(Collectors.toMap(SystemSettingModel::getSettingCode, SystemSettingModel::getSettingValue, (k1, k2) -> k1));

            String ftpPath = ssh2ConfigMap.get("ftpFilePath");
            if (StringUtils.isEmpty(ftpPath)) {
                ftpPath = ".";
            }
            ssh2ConfigMap.put("ftpFilePath",ftpPath.replaceAll("\\\\", "/"));

            if (log.isDebugEnabled()) {
                log.debug("加载SFTP配置：{}", JSON.toJSONString(ssh2ConfigMap));
            }
        }

        return ssh2ConfigMap;
    }

    @Override
    public void executeSsh(final List<String> commands) {

        if (CollectionUtils.isEmpty(commands)) {
            return;
        }

        if (log.isDebugEnabled()) {
            log.debug("ssh commands:{}", commands);
        }

        Connection conn = null;
        Session session = null;
        InputStream stdOut = null;
        InputStream stdErr = null;

        try {
            Map<String, String> settingMap = getSsh2SessionConfig();

            int port = Integer.parseInt(settingMap.get("ftpPort"));
            String host = settingMap.get("ftpServer");
            String username = settingMap.get("ftpAccount");
            String password = settingMap.get("ftpPassword");


            // 创建连接
            conn = new Connection(host, port);

            // 启动连接
            conn.connect();
            // 验证用户密码
            boolean isAuthenticated = conn.authenticateWithPassword(username, password);
            ///是否登录成功
            if (isAuthenticated == false) {
                throw new IOException("ssh Authentication failed.");
            }
            session = conn.openSession();
            // 建立虚拟终端
            session.requestPTY("bash");
            // 打开一个Shell
            session.startShell();
            stdOut = new StreamGobbler(session.getStdout());
            stdErr = new StreamGobbler(session.getStderr());
            BufferedReader stdoutReader = new BufferedReader(new InputStreamReader(stdOut));
            BufferedReader stderrReader = new BufferedReader(new InputStreamReader(stdErr));
            // 准备输入命令
            PrintWriter out = new PrintWriter(session.getStdin());
            // 输入待执行命令
            for (String line : commands) {
                out.println(line);
            }
            out.println("exit");

            // 6. 关闭输入流
            out.close();
            // 7. 等待，除非1.连接关闭；2.输出数据传送完毕；3.进程状态为退出；4.超时
            session.waitForCondition(ChannelCondition.CLOSED | ChannelCondition.EOF | ChannelCondition.EXIT_STATUS, 30000);
            log.info("Here is the output from stdout:");
            List<String> outList = new ArrayList<>();
            String line = null;
            while ((line = stdoutReader.readLine()) != null) {
                outList.add(line);
                if (log.isDebugEnabled()) {
                    log.debug("\n获取执行结果:{}", line);
                }
            }
            log.info("Here is the output from stderr:");

            List<String> errList = new ArrayList<>();
            while ((line = stderrReader.readLine()) != null) {
                errList.add(line);
                if (log.isDebugEnabled()) {
                    log.debug("\n获取执行失败结果:{}", line);
                }
            }

//            if (CollectionUtils.isEmpty(errList)) {
//                return getOkResponseResult(StringUtils.join(outList, ";"));
//            } else {
//                return getErrResponseResult(-1L, StringUtils.join(errList, ";"));
//            }

        } catch (Exception e) {
            log.info("执行sh 脚本失败".concat(e.getMessage()), e);
            throw  new RuntimeException("执行sh 脚本失败".concat(e.getMessage()));

        } finally {
            if (session != null) {
                session.close();
            }
            if (conn != null) {
                conn.close();
            }
            if (stdOut != null) {
                try {
                    stdOut.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (stdErr != null) {
                try {
                    stdErr.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    @Override
    public void copyFiles(final Map<String, String> files) {
        String ftpFilePath = getSsh2SessionConfig().get("ftpFilePath");
        List<String> commonds = new ArrayList<>();
        files.forEach((source,target)->{
            commonds.add(new StringBuilder("cp ").append(ftpFilePath).append("/").append(source).append(" ").append(ftpFilePath).append("/").append(target).toString());
        });
        executeSsh(commonds);
    }
}
